using System;
using System.Collections.Generic;
using System.Text;
using microkernel;
using Xeml.Sampling.Contracts;
using Mpi.Common.Collections;
using Xeml.Document;
using System.Diagnostics;
using System.Linq;
using Xeml.Document.Settings;

namespace Xeml.Manager
{
    //http://www.yoda.arachsys.com/csharp/singleton.html
    /// <summary>
    /// A mananger for sample provider.
    /// </summary>
    public sealed class SpManager
    {
        #region Singleton pattern.
        static readonly SpManager instance = new SpManager();

        // Explicit static constructor to tell C# compiler
        // not to mark type as beforefieldinit
        static SpManager()
        {
        }

        SpManager()
        {
            providerComponents = new Dictionary<string, IDataProvider>();
            providers = new Dictionary<string, string>();
            _messages = new List<string>();
            Debug.WriteLine("SpManager:\t before init");
            Init();
        }

        public static SpManager Instance
        {
            get
            {
                return instance;
            }
        }
        //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

        #endregion

        private Dictionary<string, IDataProvider> providerComponents;
        private Dictionary<string, string> providers; // classname: Type (of IDataProvider)

        /// <summary>
        /// A collection of available parameter resource components.
        /// key: uri of the data provider
        /// value: the data provider
        /// </summary>
        public Dictionary<string, IDataProvider> ProviderComponents
        {
            get
            {
                return this.providerComponents;
            }
        }

        /// <summary>
        /// A collection of available IDataProviders. Almost the reverse dictionary of ProviderComponents
        /// key: the dataprovider name
        /// value: uri of the dataprovider
        /// </summary>
        public Dictionary<string, string> ComponentProviders
        {
            get
            {
                return providerComponents.ToDictionary(x => x.Value.ComponentName, x => x.Key);
            }
        }

        private List<string> _messages;
        public List<string> Messages
        {
            get
            {
                return _messages;
            }
        }

        /// <summary>
        /// Initialize the manager. All available components will be loaded and component information will be read.
        /// </summary>
        private void Init()
        {
            // generate a list of possible DataProviders through reflection
            //IList<TypeReference> dataProviders = ObjectFactory.TypeList(typeof(IDataProvider));

            //foreach (TypeReference dataProvider in dataProviders)
            //{
            //    providers.Add(dataProvider.ImplementationTypeName, dataProvider.ImplementationTypeName);
            //}

            // TODO old code, remove eventually
            List<TypeReference> typeList = new List<TypeReference>();
            typeList.AddRange(ObjectFactory.TypeList(typeof(IDataProvider)));
            typeList.AddRange(ObjectFactory.TypeList(typeof(IWriteableProvider)));
            _messages.Add(string.Format("{0} type references to load", typeList.Count));
            Debug.WriteLine(string.Format("{0} type references to load", typeList.Count));
            for (int i = 0; i < typeList.Count; i++)
            {
                try
                {
                    //this extra loop here iterating "j" is to solve workitem/9305
                    for (int j = 0; j < typeList.Where(x => x.KeyTypeName == typeList[i].KeyTypeName).Count(); j++)
                    {
                        _messages.Add(string.Format("start loading provider {0} as {1}", typeList[i].ImplementationTypeName, typeList[i].KeyTypeName));
                        Debug.WriteLine(string.Format("start loading provider {0} as {1}", typeList[i].ImplementationTypeName, typeList[i].KeyTypeName));
                        IDataProvider sp = (IDataProvider)ObjectFactory.CreateInstance(typeList[i].KeyTypeName,j);
                        if (providerComponents.ContainsKey(sp.Uri))
                        {
                            if (providerComponents[sp.Uri].Version <= sp.Version)
                            {
                                providerComponents[sp.Uri] = sp;
                                _messages.Add(string.Format("provider {0} with higher version replaced", typeList[i].ImplementationTypeName));
                                Debug.WriteLine(string.Format("provider {0} with higher version replaced", typeList[i].ImplementationTypeName));
                            }
                        }
                        else
                        {
                            this.providerComponents.Add(sp.Uri, sp);
                            _messages.Add(string.Format("provider {0} loaded", typeList[i].ImplementationTypeName));
                            Debug.WriteLine(string.Format("provider {0} loaded", typeList[i].ImplementationTypeName));
                        } 
                    }
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(string.Format("provider {0} loading failed with exception '{1}'", typeList[i].ImplementationTypeName, ex.InnerException.GetType().FullName + ": " + ex.InnerException.Message));
                    _messages.Add(string.Format("provider {0} loading failed with exception '{1}'", typeList[i].ImplementationTypeName,  ex.InnerException.GetType().FullName + ": " +  ex.InnerException.Message));
                }
            }
        }

        /// <summary>
        /// Creates a sample provider.
        /// </summary>
        /// <param name="componentId">The id of the requested component.</param>
        /// <returns>A sample provider instance.</returns>
        /// <exception cref="Xeml.Component.XemlComponentLoadFailed">Will be throwed if the component can'delayTimer be found.</exception>
        public IDataProvider CreateProvider(string uri)
        {
            if (this.Contains(uri))
            {
                IDataProvider ret = null;
                try
                {
                    ret = (IDataProvider)this.providerComponents[uri].Copy();

                    //ret = (IOntologyHandler)ObjectFactory.CreateInstance(typeof(I), parameterValidatorComponents[compId].Index);
                }
                catch (Exception ex)
                {
                    throw new XemlComponentLoadFailedException(uri, "Provider exist, but loading failed", ex);
                }

                return ret;
            }
            else
            {
                _messages.Add(string.Format("unknown provider {0}", uri));
                throw new XemlComponentLoadFailedException(uri, "UnknownSample sample provider.");
            }
        }

        ///// <summary>
        ///// Creates a sample provider.
        ///// </summary>
        ///// <param name="componentId">The id of the requested component.</param>
        ///// <param name="friendlyName">A friendly name for the providerr instance.</param>
        ///// <param name="connectionString">The connection string for the provider instance.</param>
        ///// <returns>A sample provider instance.</returns>
        //public ISampleProvider CreateProvider(Guid componentId, string friendlyName, string connectionString)
        //{
        //    ISampleProvider ret = CreateProvider(componentId);
        //    ret.FriendlyName = friendlyName;
        //    ret.ConnectionString = connectionString;
        //    return ret;
        //}

        internal bool Contains(string uri)
        {
            return this.providerComponents.ContainsKey(uri);
        }
    }
}
